/**
 * 版权所有：蚂蚁与咖啡的故事 ==================================================== 文件名称: UploadServerHandler.java 修订记录： No
 * 日期				作者(操作:具体内容) 1.    2017-08-29			liuyuanxian(创建:创建文件) ====================================================
 * 类描述：(说明未实现或其它不应生成javadoc的内容)
 */
package org.lyx.file.server.support;

import java.net.URI;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import org.jboss.netty.util.CharsetUtil;
import org.lyx.file.Constants;
import org.lyx.file.server.FileServerContainer;
import org.lyx.file.server.handler.factory.FileServerHandlerFactory;
import org.lyx.file.server.parse.RequestParam;
import org.lyx.file.server.parse.RequestParamParser;
import org.lyx.file.server.utils.enumobj.EnumFileAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 *<pre><b><font color="blue">FileServerHandler</font></b></pre>
 *
 *<pre><b>文件处理核心句柄类</b></pre>
 * <pre></pre>
 * <pre>
 * <b>--样例--</b>
 *   FileServerHandler obj = new FileServerHandler();
 *   obj.method();
 * </pre>
 * @author  <b>landyChris</b>
 */
public class FileServerHandler extends SimpleChannelUpstreamHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileServerHandler.class);
    //http请求
    private HttpRequest request;
    //是否需要断点续传作业
    private boolean readingChunks;
    //接收到的文件内容
    private final StringBuffer responseContent = new StringBuffer();
    //解析收到的文件
    private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); //16384L
    //post请求的解码类,它负责把字节解码成Http请求。
    private HttpPostRequestDecoder decoder;
    //请求参数
    private RequestParam requestParams = new RequestParam();

    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
            throws Exception {
        if (this.decoder != null) {
            this.decoder.cleanFiles();
        }
    }

    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
            throws Exception {
        if (!this.readingChunks) {
            if (this.decoder != null) {
                this.decoder.cleanFiles();
                this.decoder = null;
            }
            HttpRequest request = this.request = (HttpRequest) e.getMessage();
            URI uri = new URI(request.getUri());
            //如果以form开头
            if (!uri.getPath().startsWith("/form")) {
                writeMenu(e);
                return;
            }
            try {
                //初始化decoder
                this.decoder = new HttpPostRequestDecoder(factory, request);
            } catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
                e1.printStackTrace();
                e1.printStackTrace();
                this.responseContent.append(e1.getMessage());
                writeResponse(e.getChannel());
                Channels.close(e.getChannel());
                return;
            } catch (HttpPostRequestDecoder.IncompatibleDataDecoderException e1) {
                e1.printStackTrace();
                this.responseContent.append(e1.getMessage());
                this.responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
                writeResponse(e.getChannel());
                return;
            }

            if (request.isChunked()) { //说明还没有请求完成，继续
                this.readingChunks = true;
                LOGGER.info("文件分块操作....");
            } else {
                LOGGER.info("文件大小小于1KB，文件接收完成，直接进行相应的文件处理操作....");
                //请求完成，则接收请求参数，进行初始化请求参数
                RequestParamParser.parseParams(this.decoder, this.requestParams);
                //根据请求参数进行相应的文件操作
                LOGGER.info("文件处理开始....requestParams参数解析：{}", requestParams);
                String result = FileServerHandlerFactory.process(this.requestParams);
                LOGGER.info("文件处理结束....FileServerHandlerFactory处理结果：{}", result);
                this.responseContent.append(result);
                //给客户端响应信息
                writeResponse(e.getChannel());

                e.getFuture().addListener(ChannelFutureListener.CLOSE);
            }
        } else {
            HttpChunk chunk = (HttpChunk) e.getMessage();
            try {
                //chunk.getContent().capacity();
                LOGGER.info("文件分块操作....文件大小：{} bytes", chunk.getContent().capacity());
                this.decoder.offer(chunk);
            } catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
                e1.printStackTrace();
                this.responseContent.append(e1.getMessage());
                writeResponse(e.getChannel());
                Channels.close(e.getChannel());
                return;
            }

            if (chunk.isLast()) {
                //文件末尾
                this.readingChunks = false;
                LOGGER.info("到达文件内容的末尾，进行相应的文件处理操作....start");
                RequestParamParser.parseParams(this.decoder, this.requestParams);

                LOGGER.info("文件处理开始....requestParams参数解析：{}", requestParams);
                String result = FileServerHandlerFactory.process(this.requestParams);
                LOGGER.info("文件处理结束....FileServerHandlerFactory处理结果：{}", result);

                this.responseContent.append(result);
                //给客户端响应信息
                writeResponse(e.getChannel());

                e.getFuture().addListener(ChannelFutureListener.CLOSE);
                LOGGER.info("到达文件内容的末尾，进行相应的文件处理操作....end");
            }
        }
    }

    private void writeResponse(Channel channel) {
        ChannelBuffer buf = ChannelBuffers.copiedBuffer(
                this.responseContent.toString(), CharsetUtil.UTF_8);
        this.responseContent.setLength(0);

        boolean close = ("close".equalsIgnoreCase(this.request
                .getHeader("Connection")))
                || ((this.request.getProtocolVersion()
                .equals(HttpVersion.HTTP_1_0)) && (!"keep-alive"
                .equalsIgnoreCase(this.request.getHeader("Connection"))));

        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK);
        response.setContent(buf);
        response.setHeader("Content-Type", "text/plain; charset=UTF-8");
        if (!close) {
            response.setHeader("Content-Length",
                    String.valueOf(buf.readableBytes()));
        }
        ChannelFuture future = channel.write(response);
        if (close) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    private void writeMenu(MessageEvent e) {
        this.responseContent.setLength(0);

        this.responseContent.append("<html>");
        this.responseContent.append("<head>");
        this.responseContent.append("<title>Netty Test Form</title>\r\n");
        this.responseContent.append("</head>\r\n");
        this.responseContent
                .append("<body bgcolor=white><style>td{font-size: 12pt;}</style>");
        this.responseContent.append("<table border=\"0\">");
        this.responseContent.append("<tr>");
        this.responseContent.append("<td>");
        this.responseContent.append("<h1>Netty Test Form</h1>");
        this.responseContent.append("Choose one FORM");
        this.responseContent.append("</td>");
        this.responseContent.append("</tr>");
        this.responseContent.append("</table>\r\n");

        this.responseContent
                .append("<CENTER>GET FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");

        this.responseContent
                .append("<FORM ACTION=\"/formget\" METHOD=\"POST\">");
        this.responseContent
                .append("<input type=hidden name=getform value=\"POST\">");

        this.responseContent.append("<table border=\"0\">");
        this.responseContent
                .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");

        this.responseContent
                .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");

        this.responseContent
                .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");

        this.responseContent.append("</td></tr>");
        this.responseContent
                .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");

        this.responseContent
                .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");

        this.responseContent.append("</table></FORM>\r\n");
        this.responseContent
                .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");

        this.responseContent
                .append("<CENTER>POST FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
        this.responseContent
                .append("<FORM ACTION=\"/formpost\" METHOD=\"POST\">");
        this.responseContent
                .append("<input type=hidden name=getform value=\"POST\">");
        this.responseContent.append("<table border=\"0\">");
        this.responseContent
                .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
        this.responseContent
                .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
        this.responseContent
                .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");

        this.responseContent
                .append("<tr><td>Fill with file (only file name will be transmitted): <br> <input type=file name=\"myfile\">");

        this.responseContent.append("</td></tr>");
        this.responseContent
                .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
        this.responseContent
                .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");

        this.responseContent.append("</table></FORM>\r\n");
        this.responseContent
                .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");

        this.responseContent
                .append("<CENTER>POST MULTIPART FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
        this.responseContent
                .append("<FORM ACTION=\"/formpostmultipart\" ENCTYPE=\"multipart/form-data\" METHOD=\"POST\">");
        this.responseContent
                .append("<input type=hidden name=getform value=\"POST\">");

        this.responseContent.append("<input type=hidden name=\"" + Constants.ACTION_KEY + "\" value=\""
                + EnumFileAction.UPLOAD_FILE.getValue() + "\">");
        this.responseContent.append("<table border=\"0\">");
        this.responseContent
                .append("<tr><td>账户: <br> <input type=text name=\"" + Constants.USER_NAME_KEY + "\" value=\""
                        + Constants.DEFAULT_ACCOUNT.getUserName()
                        + "\" size=10></td></tr>");
        this.responseContent
                .append("<tr><td>密码: <br> <input type=text name=\"" + Constants.PWD_KEY + "\" value=\""
                        + Constants.DEFAULT_ACCOUNT.getPwd()
                        + "\" size=10></td></tr>");
        this.responseContent
                .append("<tr><td>产生缩略图: <br> <select type=file name=\"" + Constants.THUMB_MARK_KEY + "\">");
        this.responseContent.append("<option value=\"" + Constants.THUMB_MARK_YES + "\">是</option>");
        this.responseContent.append("<option value=\"" + Constants.THUMB_MARK_NO + "\">否</option>");
        this.responseContent.append("</select></td></tr>");
        this.responseContent
                .append("<tr><td>Fill with file: <br> <input type=file name=\"" + Constants.FILE_NAME_KEY + "\">");
        this.responseContent.append("</td></tr>");
        this.responseContent
                .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
        this.responseContent
                .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");

        this.responseContent.append("</table></FORM>\r\n");
        this.responseContent
                .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");

        this.responseContent.append("</body>");
        this.responseContent.append("</html>");

        ChannelBuffer buf = ChannelBuffers.copiedBuffer(
                this.responseContent.toString(), CharsetUtil.UTF_8);

        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK);
        response.setContent(buf);
        response.setHeader("Content-Type", "text/html; charset=UTF-8");
        response.setHeader("Content-Length",
                String.valueOf(buf.readableBytes()));

        e.getChannel().write(response);
    }

    static {
        org.jboss.netty.handler.codec.http.multipart.DiskFileUpload.deleteOnExitTemporaryFile = false;
        org.jboss.netty.handler.codec.http.multipart.DiskFileUpload.baseDirectory = FileServerContainer
                .getInstance().getFileBaseDirectory();
        org.jboss.netty.handler.codec.http.multipart.DiskAttribute.deleteOnExitTemporaryFile = false;
        org.jboss.netty.handler.codec.http.multipart.DiskAttribute.baseDirectory = FileServerContainer
                .getInstance().getFileBaseDirectory();
    }
}